<template>
  <div class="app-container">
    <!-- 第一个card当中是三级联动，第二个card是属性列表相关 -->
    <el-card>
      <CategorySelector @changeCategory="changeCategory" />
    </el-card>
    <el-card style="margin-top: 20px">
      <!-- 这里是属性列表信息 -->
      <div v-show="isShowList">
        <el-button
          type="primary"
          icon="el-icon-plus"
          @click="showAddDiv"
          :disabled="!category3Id"
          >添加属性</el-button
        >
        <p />
        <el-table border :data="attrList" style="width: 100%">
          <el-table-column
            prop="prop"
            label="序号"
            type="index"
            align="center"
            width="80"
          >
          </el-table-column>

          <el-table-column prop="attrName" label="属性名称" width="150">
          </el-table-column>

          <el-table-column label="属性值列表" width="width">
            <!-- 遍历的是每个属性当中的属性值列表 -->
            <template slot-scope="{ row, $index }">
              <el-tag
                type="success"
                v-for="(attrValue, index) in row.attrValueList"
                :key="attrValue.id"
              >
                {{ attrValue.valueName }}
              </el-tag>
            </template>
          </el-table-column>

          <el-table-column prop="prop" label="操作" width="150">
            <template slot-scope="{ row, $index }">
              <HintButton
                type="warning"
                icon="el-icon-edit"
                size="mini"
                title="修改"
                @click="showUpdateDiv(row)"
              ></HintButton>
              <HintButton
                type="danger"
                icon="el-icon-delete"
                size="mini"
                title="删除"
              ></HintButton>
            </template>
          </el-table-column>
        </el-table>
      </div>

      <!-- 这里是添加、修改属性信息 -->
      <div v-show="!isShowList">
        <el-form :model="attrForm" :inline="true">
          <el-form-item label="属性名">
            <el-input
              v-model="attrForm.attrName"
              placeholder="请输入属性名称"
            ></el-input>
          </el-form-item>
        </el-form>

        <el-button
          type="primary"
          icon="el-icon-plus"
          :disabled="!attrForm.attrName"
          @click="addAttrValue"
          >添加属性值</el-button
        >
        <el-button @click="isShowList = true">取消</el-button>
        <p />

        <el-table :data="attrForm.attrValueList" border style="width: 100%">
          <el-table-column
            label="序号"
            width="80"
            type="index"
          ></el-table-column>
          <el-table-column prop="prop" label="属性值名称" width="width">
            <template slot-scope="{ row, $index }">
              <!-- @keyup.enter 与 @keyup.enter.native 之间的差异，没有 .native 不会触发事件，需要给自定义组件添加上 .native才能触发相应的事件 -->
              <el-input
                :ref="$index"
                v-model="row.valueName"
                placeholder="请输入属性值名称"
                v-if="row.isEdit"
                @blur="toLook(row)"
                @keyup.enter.native="toLook(row)"
              >
              </el-input>
              <!-- 将 span 修改成块级元素，并设置宽度高度以增加可点击操作区域 -->
              <span
                v-else
                @click="toEdit(row, $index)"
                style="display: block; width: 100%; height: 100%"
                >{{ row.valueName }}</span
              >
            </template>
          </el-table-column>
          <el-table-column label="操作" width="150">
            <template slot-scope="{ row, $index }">
              <el-popconfirm
                :title="`确定删除 ${row.valueName} 吗?`"
                @onConfirm="attrForm.attrValueList.splice($index, 1)"
              >
                <HintButton
                  slot="reference"
                  title="删除属性"
                  type="danger"
                  icon="el-icon-delete"
                  size="mini"
                >
                </HintButton>
              </el-popconfirm>
            </template>
          </el-table-column>
        </el-table>
        <p />

        <el-button type="primary" icon="el-icon-plus">保存</el-button>
        <el-button @click="isShowList = true">取消</el-button>
      </div>
    </el-card>
  </div>
</template>

<script>
import cloneDeep from "lodash/cloneDeep"
export default {
  name: "Attr",
  data() {
    return {
      // 保存子驵件传递过来的id
      category1Id: "",
      category2Id: "",
      category3Id: "",
      attrList: [],

      isShowList: true, // 控制第二个card是显示添加、修改还是列表

      attrForm: {
        attrName: "",
        attrValueList: [],
        // categoryId: this.category3Id, // 这样设值是不行的，因为data中不能使用this
        categoryId: 0,
        categoryLevel: 3,
      },
    }
  },
  methods: {
    changeCategory({ categoryId, level }) {
      console.log(level, categoryId)
      if (level === 1) {
        this.category1Id = categoryId

        // 一级被选中，需要把2、3的id数据与列表数据清空
        this.category2Id = ""
        this.category3Id = ""
        this.attrList = []
      } else if (level === 2) {
        this.category2Id = categoryId

        // 二级被选中，需要把3的id数据与列表数据清空
        this.category3Id = ""
        this.attrList = []
      } else {
        this.category3Id = categoryId

        // 三级id被选中，父组件中需要发请求获取属性列表数据
        this.getAttrList()
      }
    },
    async getAttrList() {
      let { category1Id, category2Id, category3Id } = this
      const result = await this.$API.attr.getList(
        category1Id,
        category2Id,
        category3Id
      )
      if (result.code === 200) {
        this.attrList = result.data
      }
    },
    // 点击添加按钮切换到添加页面
    showAddDiv() {
      this.isShowList = false
      // 清空收集的对象
      // 完成收集的对象剩余的收集操作
      this.attrForm = {
        attrName: "",
        attrValueList: [],
        categoryId: this.category3Id,
        categoryLevel: 3,
      }
    },
    // 点击添加属性按钮
    addAttrValue() {
      // 可以给属性值列表当中添加一个空的对象，保证表格当中首先出现一行
      // 只是添加一个空对象，属性还没有，只是占位
      this.attrForm.attrValueList.push({
        attrId: this.attrForm.id, // 这一属性y说的是属性值所属属性的id，如果是添加属性，这个id是没有的，如果是修改，则是有
        valueName: "", // 什么这个属性值有值，得让用户自己输入，输入就有值
        isEdit: true, // 添加属性值的时候，给属性对象添加标识直接就是编辑模式
      })

      // 自动获取焦点的逻辑，新增加属性值，input 需要自动获取焦点
      // 新添加的属性值是属性值列表当中的最后一项
      this.$nextTick(() => {
        this.$refs[this.attrForm.attrValueList.length - 1].focus()
      })
    },
    showUpdateDiv(row) {
      this.isShowList = false
      /*
      利用浅拷贝让修改的数据和列表展示的数据不是同一个
      但是 attrForm 中的数据内容不仅包含基础数据类型还包含对象类型
      所以，浅拷贝无法实现预期目标效果（表现形式：在进行属性列表修改时，修改属性名称与属性值，属性名称是基础数据类型，浅拷贝没问题，但是属性列表值进行修改以后，直接点击“取消”按钮，返回上一层列表，会发现列表中的数据是修改值以后的数据）
      因此，需要利用深拷贝进行对象拷贝，那就需要理清楚浅拷贝与深拷贝的区别与实现方式。(lodash功能类库)
      */
      // this.attrForm = { ...row }
      this.attrForm = cloneDeep(row) // 必须深拷贝实现复杂数据的对象复制

      // 修改属性的时候，也是每个属性值都要有自己的模式标识数据
      this.attrForm.attrValueList.forEach((item) => {
        // item.isEdit = false // 对于对象数据的修改，不能直接给对象添加新的属性值对象内容，这样的操作模式不是响应式数据
        /*
        响应式数据的内容，只有在beforeCreate、created初始化阶段的数据才是响应式数据
        此时点击按钮给响应式对象添加新的属性数据，不是响应式的数据，这样会导致后期添加了以后页面不会发生变化
        如果后期添加的属性要响应式，必须使用 Vue.set 或者 vm.$set  才能让其成为响应式数据
        */
        this.$set(item, "isEdit", false)
      })
    },
    // input 失去焦点或者input回车之后切换 span
    toLook(row) {
      // 在 input 变为 span 之前，需要校验用户输入数据的合法性
      // 1.用户输入的数据不能为空
      let valueName = row.valueName
      if (!valueName.trim()) {
        this.$message.info("输入的属性名称不能为空")
        row.valueName = "" // 清空输入框不合法数据
        return
      }
      // 2.用户输入的数据不能和已有的其它属性名称重复，需要循环判断是否相同，可以利用数组的 some 函数
      let isRepeat = this.attrForm.attrValueList.some((item) => {
        if (item !== row) {
          // 除去自身之后，有一个达到 return 条件，那么就返回 true
          return item.valueName === valueName
        }
      })

      if (isRepeat) {
        this.$message.info("输入的属性名称不能重复")
        row.valueName = "" // 清空输入框不合法数据
        return
      }
      // 这里可以直接点操作，并且修改 isEdit 的数据，因为之前添加属性值的时候已经是响应式数据
      row.isEdit = false
    },
    // span 点击以后，切换成 input
    toEdit(row, index) {
      row.isEdit = true

      // 自动获取焦点的功能，根据 index 就知道是哪个 input 要获取焦点，并且这个 index 刚好就是对应的 input 的 ref 值
      /*
      step 1
      为何 this.$refs[index] 是 undefined
      因为我们获取这个节点获取的太快
      将 row.isEdit = true 改完之后，由于 DOM 元素是通过 vif 才开始创建，紧接着你就获取这个元素，此时 input 还没创建好，所以获取到的是u ndefined
      */
      // this.$refs[index].focus()

      /*
      step 2
      利用 nextTick 进行 DOM 渲染后的操作处理
      nextTick 与 updated 的区别
      nextTick 页面的最近一次更新完成之后执行回调，一次就完了
      updated 只要页面有数据更新，就会执行
      */
      this.$nextTick(() => {
        this.$refs[index].focus()
      })
    },
  },
}
</script>

<style lang="scss" scoped>
</style>
